/*
 * 百度在线网络技术（北京）有限公司拥有本软件版权2022并保留所有权利。
 * Copyright 2022, Baidu.com,Inc 2:Baidu Online Network Technology (Beijing) Co.,Ltd,
 * All rights reserved.
 */

package com.azdebugit.sync.hronized.waitnotiy1;


/**
 * java多线程同步
 * 1. synchronized修饰方法[参见syncFunOne]
 * 2. synchronized同步块(普通类成员变量)[参见syncFunTwo]
 * 3. synchronized同步块(static类成员变量)[参见syncFunThree]
 * 4. wait()及notify()的用法[参见syncFunFour1、syncFunFour2]
 *
 * @author cheng2839
 * @date 2018年11月16日
 */
public class MyThreadTest001 {

    //此处定义一个静态类成员变量，供所有线程共用[共用信号量]
    public static int NUM;

    //Object成员变量
    private Object lock = new Object();

    //Object静态类成员变量
    private static final Object LOCK = new Object();

    //初始化信号量值
    public MyThreadTest001() {
        NUM = 1;
    }

    /**
     * synchronize修饰的方法，表示该方法是同步的
     */
    public synchronized void syncFunOne() {
        System.out.println(Thread.currentThread()+" - syncFunOne before : " + NUM);
        NUM++;
        System.out.println(Thread.currentThread()+" - syncFunOne after : " + NUM);
    }

    /**
     * synchronize修饰的代码块，表示该方法中被synchronized修饰的代码块是同步的
     */
    public void syncFunTwo() {
        synchronized(lock) {
            System.out.println(Thread.currentThread()+" - syncFunTwo before : " + NUM);
            NUM++;
            System.out.println(Thread.currentThread()+" - syncFunTwo after : " + NUM);
        }
    }

    /**
     * synchronize修饰的代码块，表示该方法中被synchronized修饰的代码块是同步的
     */
    public void syncFunThree() {
        synchronized(LOCK) {
            System.out.println(Thread.currentThread()+" - syncFunThree before : " + NUM);
            NUM++;
            System.out.println(Thread.currentThread()+" - syncFunThree after : " + NUM);
        }
    }

    /**
     * lock.wait()表示等待被lock对象实例的notify()唤醒，才可继续执行
     * 注意：使用wait()必须被synchronized修饰
     *       wait()和notify()配对使用，表示在执行上有先后顺序
     */
    public void syncFunFour1() {
        try {
            while (true) {
                synchronized (lock) {
                    lock.wait(); //等待被唤醒
                    System.out.println(Thread.currentThread()+" - syncFunFour1 before : " + NUM);
                    NUM++;
                    System.out.println(Thread.currentThread()+" - syncFunFour1 after : " + NUM);
                }
            }
        }catch (InterruptedException e){}
    }

    /**
     * lock.notify()表示实例唤醒一个被lock对象wait()的线程
     * 注意：使用notify()必须被synchronized修饰
     *       wait()和notify()配对使用，表示在执行上有先后顺序
     */
    public void syncFunFour2() {
        try {
            while (true) {
                synchronized (lock) {
                    System.out.println(Thread.currentThread()+" - syncFunFour2 before : " + NUM);
                    NUM--;
                    System.out.println(Thread.currentThread()+" - syncFunFour2 after : " + NUM);
                    lock.notify(); //唤醒一个wait的线程
                }
                System.out.println(Thread.currentThread()+" - sleep beginning.");
                Thread.sleep(3000);
                System.out.println(Thread.currentThread()+" - sleep finished.");
            }
        }catch (InterruptedException e){}
    }

    /**
     * 测试wait()和notify()
     * @param test001
     */
    public void testWait(MyThreadTest001 test001){
        //执行顺序是：对信号量先减1，再唤醒加1线程，每3秒循环执行一次
        new Thread(() -> test001.syncFunFour1()).start(); //对信号量进行加1操作
        new Thread(() -> test001.syncFunFour2()).start(); //对信号量进行减1操作
    }


    public static void main(String[] args) {
        MyThreadTest001 test001 = new MyThreadTest001();

        //测试synchronized
        //创建threadCount个线程，对同一信号量进行加1操作，保证信号量同时只被一个线程操作，保证了信号量的一致性
        int threadCount = 5;
        while (threadCount-->0) {
            Thread thread = new Thread(new TestThread(test001));
            thread.start();
        }

        //测试wait()和notify()
//        test001.testWait(test001);

    }
}

/**
 * 创建一个线程
 */
class TestThread implements Runnable{
    private MyThreadTest001 test001;
    public TestThread(MyThreadTest001 test001) {
        this.test001 = test001;
    }
    public void run() {
        try {
            //每个线程每隔50毫秒并循环调用5次
            for (int i=0;i<5;i++) {
                test001.syncFunOne(); //此处可以切换为syncFunTwo、syncFunThree进行测试
//                test001.syncFunTwo();
//                test001.syncFunThree();
                Thread.sleep(50);
            }
        }catch (InterruptedException e) {}
    }
}